#!/usr/bin/python3
# -*- coding:utf-8 -*-
import argparse
from ctypes import *
import os
import sys
import codecs
import json


class Dbkit:
    lib = None

    def __init__(self):
        self.__loadLibrary("Dbkit")
        self.__configLibrary()
        self.lib.dbkit_new()

    def __del__(self):
        self.lib.dbkit_delete()

    def __loadLibrary(self, libname):
        libfile = libname
        if sys.platform == "linux" or sys.platform == "linux2":
            libfile = "./lib" + libname + ".so"
        elif sys.platform == "darwin":
            libfile = libname + ".dylyb"
        elif sys.platform == "win32":
            libfile = libname + ".dll"
        libpath = os.path.join(os.path.dirname(__file__), libfile)
        self.lib = cdll.LoadLibrary(libpath)

    def __configLibrary(self):
        self.lib.dbkit_new.argtypes = []
        self.lib.dbkit_new.restype = c_int

        self.lib.dbkit_delete.argtypes = []
        self.lib.dbkit_delete.restype = c_int

        self.lib.dbkit_execute.argtypes = [c_char_p]
        self.lib.dbkit_execute.restype = c_int

        self.lib.dbkit_executeScript.argtypes = [c_char_p]
        self.lib.dbkit_executeScript.restype = c_int

        self.lib.dbkit_connect.argtypes = [c_char_p]
        self.lib.dbkit_connect.restype = c_int

        self.lib.dbkit_disconnect.argtypes = []
        self.lib.dbkit_disconnect.restype = c_int

        self.lib.dbkit_setKey.argtypes = [c_char_p]
        self.lib.dbkit_setKey.restype = c_int

        self.lib.dbkit_setMode.argtypes = [c_char_p]
        self.lib.dbkit_setMode.restype = c_int

        self.lib.dbkit_setEvaluation.argtypes = [c_char_p]
        self.lib.dbkit_setEvaluation.restype = c_int

        self.lib.dbkit_setCheckpoint.argtypes = [c_char_p]
        self.lib.dbkit_setCheckpoint.restype = c_int

        self.lib.dbkit_json_insertRows.argtypes = [c_char_p, POINTER(c_char_p)]
        self.lib.dbkit_json_insertRows.restype = c_int

        self.lib.dbkit_json_deleteRows.argtypes = [c_char_p, POINTER(c_char_p)]
        self.lib.dbkit_json_deleteRows.restype = c_int

        self.lib.dbkit_json_updateRows.argtypes = [c_char_p, POINTER(c_char_p)]
        self.lib.dbkit_json_updateRows.restype = c_int

        self.lib.dbkit_json_selectRows.argtypes = [c_char_p, POINTER(c_char_p)]
        self.lib.dbkit_json_selectRows.restype = c_int

        self.lib.dbkit_json_selectJoinedRow.argtypes = [c_char_p, POINTER(c_char_p)]
        self.lib.dbkit_json_selectJoinedRow.restype = c_int

        self.lib.dbkit_json_getTables.argtypes = [c_char_p, POINTER(c_char_p)]
        self.lib.dbkit_json_getTables.restype = c_int

        self.lib.dbkit_json_getColumns.argtypes = [c_char_p, POINTER(c_char_p)]
        self.lib.dbkit_json_getColumns.restype = c_int

        self.lib.dbkit_json_count.argtypes = [c_char_p, POINTER(c_char_p)]
        self.lib.dbkit_json_count.restype = c_int

        self.lib.dbkit_json_sum.argtypes = [c_char_p, POINTER(c_char_p)]
        self.lib.dbkit_json_sum.restype = c_int

    def execute(self, command):
        return self.lib.dbkit_execute(self.__encode(command))

    def executeScript(self, script):
        return self.lib.dbkit_executeScript(self.__encode(script))

    def connect(self, databasePath):
        return self.lib.dbkit_connect(self.__encode(databasePath))

    def disconnect(self):
        return self.lib.dbkit_disconnect()

    def setKey(self, key):
        return self.lib.dbkit_setKey(self.__encode(key))

    def setMode(self, mode):
        return self.lib.dbkit_setMode(self.__encode(mode))

    def setEvaluation(self, evaluation):
        return self.lib.dbkit_setEvaluation(self.__encode(evaluation))

    def setCheckpoint(self, checkpoint):
        return self.lib.dbkit_setCheckpoint(self.__encode(checkpoint))

    def jsonInsertRows(self, inputJson):
        return self.__jsonWrapper(inputJson, self.lib.dbkit_json_insertRows)

    def jsonDeleteRows(self, inputJson):
        return self.__jsonWrapper(inputJson, self.lib.dbkit_json_deleteRows)

    def jsonUpdateRows(self, inputJson):
        return self.__jsonWrapper(inputJson, self.lib.dbkit_json_updateRows)

    def jsonSelectRows(self, inputJson):
        return self.__jsonWrapper(inputJson, self.lib.dbkit_json_selectRows)

    def jsonSelectJoinedRow(self, inputJson):
        return self.__jsonWrapper(inputJson, self.lib.dbkit_json_selectJoinedRow)

    def jsonGetTables(self, inputJson):
        return self.__jsonWrapper(inputJson, self.lib.dbkit_json_getTables)

    def jsonGetColumns(self, inputJson):
        return self.__jsonWrapper(inputJson, self.lib.dbkit_json_getColumns)

    def jsonCount(self, inputJson):
        return self.__jsonWrapper(inputJson, self.lib.dbkit_json_count)

    def jsonSum(self, inputJson):
        return self.__jsonWrapper(inputJson, self.lib.dbkit_json_sum)

    @staticmethod
    def __jsonWrapper(inputJson, jsonOp):
        c_output = c_char_p()
        r = jsonOp(
            Dbkit.__encode(Dbkit.__stringify(inputJson)),
            byref(c_output))
        return r, Dbkit.__jsonify(Dbkit.__decode(c_output))

    @staticmethod
    def __encode(s):
        return c_char_p(bytes(s, encoding='utf-8'))

    @staticmethod
    def __decode(c_s):
        return str(c_s.value, encoding='utf-8')

    @staticmethod
    def __jsonify(s):
        return json.loads(s)

    @staticmethod
    def __stringify(j):
        return json.dumps(j, ensure_ascii=False)


def __main():
    # Show options
    parser = argparse.ArgumentParser(
        description="Toolkit for processing mathematical operations on database")
    parser.add_argument(
        "-m", "--mode", choices=['train', 'predict'], default='train',
        help="set mode for dbkit")
    parser.add_argument(
        "-d", "--database",
        help="connect to the specified database")
    parser.add_argument(
        "-c", "--checkpoint",
        help="store training meta-data")
    group = parser.add_mutually_exclusive_group()
    group.add_argument(
        "-i", "--interactive", action="store_true", default=False,
        help="run as an interactive interpreter")
    group.add_argument(
        "-s", "--script",
        help="execute scripts from the specified script file")
    parser.add_argument(
        "-v", "--verbose", action="store_true", default=False,
        help="show progress of execution of script (use `-vs` instead of `-sv`)")
    args = parser.parse_args()

    # Parse options
    dbkit = None
    if args.interactive is not None or args.script is not None:
        dbkit = Dbkit()
        if args.mode:
            dbkit.setMode(args.mode)
        if args.database:
            dbkit.connect(args.database)
        if args.checkpoint:
            dbkit.setCheckpoint(args.checkpoint)
    if args.interactive:
        while True:
            line = input(">>> ").strip()
            if len(line) == 0 or line[0] == '#':
                continue
            elif line in 'quit' or line in 'exit' or line in 'done':
                break
            else:
                dbkit.execute(line)
    if args.script:
        if args.verbose:
            print("_____________________________")
            print("|                           /")
            print("| Let's begin.             /")
            print("| ________________________/")
            print("| |")
            fileScript = codecs.open(args.script, 'r', 'utf-8')  # 以utf-8编码打开配制文件
            lines = [line.strip() for line in fileScript.readlines()]  # 获取文件所有内容
            fileScript.close()  # 有始有终
            for line in lines:
                if len(line) == 0 or line[0] == '#':
                    continue
                print("| |  Start processing: \"" + line + "\"")
                if dbkit.execute(line) == 1:
                    print("| |  Finished!")
                else:
                    print("| |  Error!")
            print("| |________________________")
            print("|                          \\")
            print("| Well done!                \\")
            print("|____________________________\\")
        else:
            print("Start executing {}... ".format(args.script), end='')
            dbkit.executeScript(args.script)
            print("Done!")


if __name__ == '__main__':
    __main()
